﻿#include "RecipeInfo.h"
#include "../Settings.h"

using namespace Server::MirEnvir;
using namespace Server::MirObjects;

namespace Server::MirDatabase
{

    Server::MirEnvir::Envir *RecipeInfo::getEnvir()
    {
        return Envir::getMain();
    }

    Server::MessageQueue *RecipeInfo::getMessageQueue()
    {
        return MessageQueue::getInstance();
    }

    RecipeInfo::RecipeInfo(const std::string &name)
    {
        ItemInfo *itemInfo = getEnvir()->GetItemInfo(name);
        if (itemInfo == nullptr)
        {
            getMessageQueue()->Enqueue(StringHelper::formatSimple("Could not find Item: {0}", name));
            return;
        }

        setEnvir(getEnvir() + 1);
        Item = getEnvir()->CreateShopItem(itemInfo, getEnvir()->NextRecipeID);

        LoadIngredients(name);
    }

    void RecipeInfo::LoadIngredients(const std::string &recipe)
    {
        std::vector<std::string> lines = File::ReadAllLines(FileSystem::combine(Settings::RecipePath, recipe + ".txt")).ToList();

        Tools = std::vector<UserItem*>();
        Ingredients = std::vector<UserItem*>();

        auto mode = "ingredients";

        for (int i = 0; i < lines.size(); i++)
        {
            if ((lines[i]).empty())
            {
                continue;
            }

            if (StringHelper::startsWith(lines[i], "["))
            {
                mode = StringHelper::toLower(lines[i].substr(1, lines[i].length() - 2));
                continue;
            }

//C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string and was converted to C++ 'if-else' logic:
//            switch (mode)
//ORIGINAL LINE: case "recipe":
            if (mode == "recipe")
            {
                        auto data = lines[i].Split(std::vector<char> {' '}, StringSplitOptions::RemoveEmptyEntries);

                        if (data.size() < 2)
                        {
                            continue;
                        }

//C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string and was converted to C++ 'if-else' logic:
//                        switch (data[0].ToLower())
//ORIGINAL LINE: case "amount":
                        if (StringHelper::toLower(data[0]) == "amount")
                        {
                                Item->Count = static_cast<unsigned short>(data[1]);
                        }
//ORIGINAL LINE: case "chance":
                        else if (StringHelper::toLower(data[0]) == "chance")
                        {
                                Chance = static_cast<unsigned char>(data[1]);

                                if (Chance > 100)
                                {
                                    Chance = 100;
                                }
                        }
//ORIGINAL LINE: case "gold":
                        else if (StringHelper::toLower(data[0]) == "gold")
                        {
                                Gold = std::stoul(data[1]);
                        }
                        else
                        {
                        }
            }
//ORIGINAL LINE: case "tools":
            else if (mode == "tools")
            {
                        auto data = lines[i].Split(std::vector<char> {' '}, StringSplitOptions::RemoveEmptyEntries);

                        ItemInfo *info = getEnvir()->GetItemInfo(data[0]);

                        if (info == nullptr)
                        {
                            getMessageQueue()->Enqueue(StringHelper::formatSimple("Could not find Tool: {0}, Recipe: {1}", lines[i], recipe));
                            continue;
                        }

                        UserItem *tool = getEnvir()->CreateShopItem(info, 0);

                        Tools.push_back(tool);
            }
//ORIGINAL LINE: case "ingredients":
            else if (mode == "ingredients")
            {
                        auto data = lines[i].Split(std::vector<char> {' '}, StringSplitOptions::RemoveEmptyEntries);

                        ItemInfo *info = getEnvir()->GetItemInfo(data[0]);

                        if (info == nullptr)
                        {
                            getMessageQueue()->Enqueue(StringHelper::formatSimple("Could not find Ingredient: {0}, Recipe: {1}", lines[i], recipe));
                            continue;
                        }

                        UserItem *ingredient = getEnvir()->CreateShopItem(info, 0);

                        unsigned short count = 1;
                        if (data.size() >= 2)
                        {
                            unsigned short::TryParse(data[1], count);
                        }

                        if (data.size() >= 3)
                        {
                            unsigned short::TryParse(data[2], ingredient->CurrentDura);
                        }

                        ingredient->Count = count > info->StackSize ? info->StackSize : count;

                        Ingredients.push_back(ingredient);
            }
//ORIGINAL LINE: case "criteria":
            else if (mode == "criteria")
            {
                        auto data = lines[i].Split(std::vector<char> {' '}, StringSplitOptions::RemoveEmptyEntries);

                        if (data.size() < 2)
                        {
                            continue;
                        }

                        try
                        {
//C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string and was converted to C++ 'if-else' logic:
//                            switch (data[0].ToLower())
//ORIGINAL LINE: case "level":
                            if (StringHelper::toLower(data[0]) == "level")
                            {
                                    RequiredLevel = std::make_optional(static_cast<unsigned short>(data[1]));
                            }
//ORIGINAL LINE: case "class":
                            else if (StringHelper::toLower(data[0]) == "class")
                            {
                                    MirClass cls;
                                    if (Enum::TryParse<MirClass>(data[1], true, cls))
                                    {
                                        RequiredClass.push_back(cls);
                                    }
                                    else
                                    {
                                        RequiredClass.push_back(static_cast<MirClass>(static_cast<unsigned char>(data[1])));
                                    }
                            }
//ORIGINAL LINE: case "gender":
                            else if (StringHelper::toLower(data[0]) == "gender")
                            {
                                    MirGender gender;
                                    if (Enum::TryParse<MirGender>(data[1], true, gender))
                                    {
                                        RequiredGender = std::make_optional(gender);
                                    }
                                    else
                                    {
                                        RequiredGender = std::make_optional(static_cast<MirGender>(static_cast<unsigned char>(data[1])));
                                    }
                            }
//ORIGINAL LINE: case "flag":
                            else if (StringHelper::toLower(data[0]) == "flag")
                            {
                                    RequiredFlag.push_back(std::stoi(data[1]));
                            }
//ORIGINAL LINE: case "quest":
                            else if (StringHelper::toLower(data[0]) == "quest")
                            {
                                    RequiredQuest.push_back(std::stoi(data[1]));
                            }
                        }
                        catch (...)
                        {
                            getMessageQueue()->Enqueue(StringHelper::formatSimple("Could not parse option: {0}, Value: {1}", data[0], data[1]));
                            continue;
                        }
            }
        }
    }

    bool RecipeInfo::MatchItem(int index)
    {
        return Item != nullptr && Item->ItemIndex == index;
    }

    bool RecipeInfo::CanCraft(PlayerObject *player)
    {
        if (RequiredLevel && RequiredLevel.value() > player->getLevel())
        {
            return false;
        }

        if (RequiredGender && RequiredGender.value() != player->getGender())
        {
            return false;
        }

        if (RequiredClass.size() > 0 && !std::find(RequiredClass.begin(), RequiredClass.end(), player->getClass()) != RequiredClass.end())
        {
            return false;
        }

        if (RequiredFlag.size() > 0)
        {
            for (auto flag : RequiredFlag)
            {
                 if (!player->Info->Flags[flag])
                 {
                    return false;
                 }
            }
        }

        if (RequiredQuest.size() > 0)
        {
            for (auto quest : RequiredQuest)
            {
                if (!std::find(player->Info->CompletedQuests.begin(), player->Info->CompletedQuests.end(), quest) != player->Info->CompletedQuests.end())
                {
                    return false;
                }
            }
        }

        return true;
    }

    ClientRecipeInfo *RecipeInfo::CreateClientRecipeInfo()
    {
        ClientRecipeInfo *clientInfo = new ClientRecipeInfo { Gold = Gold, Chance = Chance, Item = Item->Clone(), Tools = Tools.Select([&] (std::any x)
        {
            x;
        }).ToList(), Ingredients = Ingredients.Select([&] (std::any x)
        {
            x;
        }).ToList() };

//C# TO C++ CONVERTER TODO TASK: A 'delete clientInfo' statement was not added since clientInfo was used in a 'return' or 'throw' statement.
        return clientInfo;
    }
}
